/* eslint-disable no-unused-vars */
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable no-console */
import { useEffect } from 'react';
import { proxy, ref, useSnapshot } from 'valtio';

// 通过实验观察的然后猜想，效率是低的，需要覆盖更多可能性
// 在代码中调试，效率是最低的，变量完全是不可控的
// 猜想的前提是覆盖所有的情况，否则就是盲人摸象，每个阶段的猜想都会被下一个阶段颠覆和补充
// 源码学习 > demo实验+猜想 > 源码调试+猜想
// 为了让问题得到高效的解答，我们务必使用有效解决问题的方法

class PanelService {
  declare root: any;
  // key = 1;
  key: string | number = 'panel';
  otherKey:string|number = 'otherKey';
  bindRoot(root: Root) {

    // 第一阶段猜想+第二阶段补充
    // 情况1: 如果 这句（修改非依赖属性）+没有访问其他属性( useSnapshot(service) )，会额外触发1次render
    //       这种为非推荐用法，不具体分析，大概因为会监听所有属性的变化，具体见原则1
    // 情况2: 如果 这句（修改非依赖属性）+访问了其他属性（  useSnapshot(service) + snap.xxx  ），会额外触发2次render
    //       发现可通过 ref(proxy(service))的方式可以避免重复render
    //       重点分析为什么会出现2次render？
    //       不看源码能给出准确的猜想吗？
    //       这种是标准用法，所以需要解释
    //       也许需要考虑销毁策略
    // 情况3: 如果 没有这句+不管是否访问了其他属性，不会再次触发render
    //       service属性没有变化，则不会触发render
    // 情况4: 如果 有这句+ 没有任何访问，不会再次触发render
    //       没有手机依赖项，则不会触发render

    this.root = root;
    // this.root = ref(root);
    // this.otherKey = 1;
    // this.changeOtherKey();
  }

  changeKey() {
    this.key = Math.random();
  }

  changeOtherKey(){
    this.otherKey = Math.random();
  }
}

class Root {
  key = 'root';
  // 第三阶段发现，可能是测试过程中的失误，错误的的总结了这个现象，其实不存在ref(proxy(new PanelService()))可以减少render次数
  // 第一阶段猜想+第二阶段补充
  // 可以减少useEffect中变更导致的不必要的render
  // panel = ref(proxy(new PanelService()));
  panel = new PanelService();

  init() {
    this.panel.bindRoot(this);
    // this.panel.changeOtherKey();
    // this.panel.otherKey = '1';
  }
}

const Editor = ({ service }: { service: Root }) => {
  console.count('Editor render');
  // 第二阶段发现
  // 原则1: useSnapshot 应该与 snap.xxx 成对使用
  //
  // 情况1: 如果成对使用，则只有依赖项（snap.xxx）的变更会触发rerender
  //
  // 情况2: 如果只有useSnapshot(service)，则service中任意属性变化，都会触发rerender
  //        这次 render 发生在子组件render完成之后
  //        不应该这样使用，这会导致很多额外的render
  //        如果要监听对像引用的变化可以使用useEffect
  //
  // 说明 useSnapshot本身就会收集依赖，并影响rerender机制

  // 调整注释后点击按钮【change panel key】试试
  // const snap = useSnapshot(service);
  // console.log(snap.key);

  useEffect(() => {
    /** 在useEffect中的调用会导致额外的render */
    service.init();
  }, [service]);

  return <div >
    Root:
    <button onClick={() => { service.panel.changeKey(); }}>change panel key</button>
    {/* 下面2种调用都不会触发额外的render */}
    <button onClick={() => { service.panel.changeOtherKey(); }}>change other key</button>
    <button onClick={() => { service.init(); }}>change other key (init)</button>
  </div>;
};

const Panel = ({ service }: { service: PanelService }) => {
  const snap = useSnapshot(service);
  console.count('Panel render');

  return <div >
    Panel:
    <button onClick={() => { service.changeKey(); }}>change panel key</button>
    <button onClick={() => { service.changeOtherKey(); }}>change other key</button>

    {snap.key}
    {snap.root?.key}
  </div>;

};

const root = proxy(new Root);

const Test = () => {
  console.count('page count');
  return <div >
    <Editor service={root} />
    <Panel service={root.panel} />
  </div>;
};

export default Test;
